from django.core.files.storage import Storage
from django.conf import settings
from fdfs_client.client import Fdfs_client


class FastDFSStorage(Storage):
    """自定义文件存储类:实现文件被转存到FDFS中"""

    def __init__(self, client_conf=None, base_url=None):
        """
        构造方法
        django在进行文件存储时会自动的调用构造方法
        但是,"不会传入参数"
        """
        self.client_conf = client_conf or settings.FDFS_CLIENT_CONF
        self.base_url = base_url or settings.FDFS_BASE_URL

    def _open(self, name, mode='rb'):
        """
        重写此方法主要用于文件的打开下载
        :param name: 文件名
        :param mode: 打开模式:二进制读取
        :return:
        """
        pass

    def _save(self, name, content):
        """
        重写此方法用于文件的上传
        Django会将该方法的返回值保存到数据库中对应的文件字段ImageField
        也就是说该方法应该返回要保存在数据库中的文件名称信息
        :param name: 文件名
        :param content: content需要是一个文件对象File
        :return:
        """
        # 实例化fdfs_client客户端,并传入配置文件路径
        client = Fdfs_client(self.client_conf)

        # 上传文件,因为content为文件对象,网络中文件的上传下载均需要二进制的形式进行,所以
        # 通过read读取成二进制格式进行upload
        ret = client.upload_by_buffer(content.read())

        # 判断是否上传成功
        if ret.get('Status') != 'Upload successed.':
            raise Exception('Upload files failed')

        # 获取文件在存储服务器中保存的id,模型类会自动读取这个id,存储的ImageField字段中
        file_id = ret.get('Remote file_id')
        return file_id

    def exists(self, name):
        """
        判断该文件是否已再本地存储
        如果已在本地存储则返回True
        如果不在本地存储,则返回False
        因为fastDFS可以自行解决文件的重名问题
        所以此方法应始终返回False,django才会积极的去存储文件
        :param name: 文件名
        :return:
        """
        return False

    def url(self, name):
        """
        返回文件的url绝对路径
        可以通过模型类相应字段ImageField调用此方法
        :param name: 要返回的全路径的文件的名字
        :return: 返回可以通过浏览器直接进行访问的绝对url路径
        """
        # 'http://192.168.31.142:8888/'
        return self.base_url + name
